package server;

/**
 *
 * @author pquiring
 *
 * Created : Mar 15, 2015
 */

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

import javaforce.*;

public class Server extends javax.swing.JFrame implements ActionListener {

  /**
   * Creates new form Server
   */
  public Server() {
    initComponents();
    //create tray icon to open app
    JFImage img = new JFImage();
    img.load(getClass().getResourceAsStream("/jfrdp.png"));
    setIconImage(img.getImage());
    img.load(getClass().getResourceAsStream("/jfrdp-tray.png"));
    PopupMenu popup = new PopupMenu();
    show = new MenuItem("Show");
    show.addActionListener(this);
    popup.add(show);
    popup.addSeparator();
    exit = new MenuItem("Exit");
    exit.addActionListener(this);
    popup.add(exit);
    icon = new TrayIcon(img.getImage(), "RDP", popup);
    icon.addActionListener(this);
    tray = SystemTray.getSystemTray();
    try { tray.add(icon); } catch (Exception e) { JFLog.log(e); }
    //start service
    JF.initHttps();
    readConfig();
    JFAWT.centerWindow(this);
    if (sslValid()) {
      service = new Service();
      service.start(webPassword.getText(), rdpPassword.getText(), (Integer)maxUsers.getValue(), viewOnly.isSelected());
    }
  }

  public void readConfig() {
    try {
      Properties p = new Properties();
      FileInputStream fis = new FileInputStream(JF.getUserPath() + "/.jfrdp-server.cfg");
      p.load(fis);
      fis.close();
      String apass = p.getProperty("adminPassword");
      if (apass == null) {
        apass = p.getProperty("adminEncodedPassword");
        if (apass == null) {
          apass = "";
        } else {
          try {
            apass = decodePassword(apass);
          } catch (Exception e) {
            apass = "";
          }
        }
      }
      adminPassword.setText(apass);
      String wpass = p.getProperty("webPassword");
      if (wpass == null) {
        wpass = p.getProperty("webEncodedPassword");
        if (wpass == null) {
          wpass = "";
        } else {
          try {
            wpass = decodePassword(wpass);
          } catch (Exception e) {
            wpass = "";
          }
        }
      }
      webPassword.setText(wpass);
      String rpass = p.getProperty("rdpPassword");
      if (rpass == null) {
        rpass = p.getProperty("rdpEncodedPassword");
        if (rpass == null) {
          rpass = "";
        } else {
          try {
            rpass = decodePassword(rpass);
          } catch (Exception e) {
            rpass = "";
          }
        }
      }
      rdpPassword.setText(rpass);
      String users = p.getProperty("maxusers");
      if (users != null) {
        maxUsers.setValue(JF.atoi(users));
      }
      String view = p.getProperty("viewonly");
      if (view != null && view.equals("true")) {
        viewOnly.setSelected(true);
      }
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  public void writeConfig() {
    try {
      Properties p = new Properties();
      p.setProperty("adminEncodedPassword", encodePassword(adminPassword.getText()));
      p.setProperty("webEncodedPassword", encodePassword(webPassword.getText()));
      p.setProperty("rdpEncodedPassword", encodePassword(rdpPassword.getText()));
      p.setProperty("maxusers", ((Integer)maxUsers.getValue()).toString());
      p.setProperty("viewonly", viewOnly.isSelected() ? "true" : "false");
      FileOutputStream fos = new FileOutputStream(JF.getUserPath() + "/.jfrdp-server.cfg");
      p.store(fos, "#jfRDP-server");
      fos.close();
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  public void restart() {
    if (service != null) {
      service.close();
      service = null;
      JF.sleep(1000);
    }
    if (sslValid()) {
      service = new Service();
      service.start(webPassword.getText(), rdpPassword.getText(), (Integer)maxUsers.getValue(), viewOnly.isSelected());
    }
  }

  public void actionPerformed(ActionEvent e) {
    Object o = e.getSource();
    if (o == exit) {
      System.exit(0);
    }
    if (o == show) {
      readConfig();
      if (adminPassword.getText().length() > 0) {
        while (true) {
          GetPassword dialog = new GetPassword(this, true);
          dialog.setVisible(true);
          if (!dialog.accepted) {
            return;
          }
          if (!dialog.getPassword().equals(adminPassword.getText())) {
            JFAWT.showError("Error", "Incorrect password");
            continue;
          } else {
            break;
          }
        }
      }
      setVisible(true);
    }
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    save = new javax.swing.JButton();
    Cancel = new javax.swing.JButton();
    jLabel1 = new javax.swing.JLabel();
    jLabel2 = new javax.swing.JLabel();
    webPassword = new javax.swing.JTextField();
    jLabel3 = new javax.swing.JLabel();
    rdpPassword = new javax.swing.JTextField();
    generateSSL = new javax.swing.JButton();
    status = new javax.swing.JLabel();
    donate = new javax.swing.JButton();
    jLabel4 = new javax.swing.JLabel();
    adminPassword = new javax.swing.JTextField();
    jLabel5 = new javax.swing.JLabel();
    maxUsers = new javax.swing.JSpinner();
    viewOnly = new javax.swing.JCheckBox();

    setTitle("jfRDP Server");

    save.setText("Save and Restart");
    save.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        saveActionPerformed(evt);
      }
    });

    Cancel.setText("Cancel");
    Cancel.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        CancelActionPerformed(evt);
      }
    });

    jLabel1.setText("RDP Configuration:");

    jLabel2.setText("Web Password");

    jLabel3.setText("RDP Password");

    generateSSL.setText("Generate SSL Certificate");
    generateSSL.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        generateSSLActionPerformed(evt);
      }
    });

    status.setText("Status : ...");

    donate.setText("Donate");
    donate.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        donateActionPerformed(evt);
      }
    });

    jLabel4.setText("Admin Password");

    jLabel5.setText("Max Concurrent Users :");

    maxUsers.setModel(new javax.swing.SpinnerNumberModel(1, 1, 20, 1));

    viewOnly.setText("View Only");

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addComponent(generateSSL)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(donate)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 37, Short.MAX_VALUE)
            .addComponent(Cancel)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(save))
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(jLabel2)
              .addComponent(jLabel3))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(rdpPassword)
              .addComponent(webPassword)))
          .addComponent(status, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
          .addGroup(layout.createSequentialGroup()
            .addComponent(jLabel4)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(adminPassword))
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(jLabel1)
              .addGroup(layout.createSequentialGroup()
                .addComponent(jLabel5)
                .addGap(18, 18, 18)
                .addComponent(maxUsers, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE))
              .addComponent(viewOnly))
            .addGap(0, 0, Short.MAX_VALUE)))
        .addContainerGap())
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(jLabel1)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel4)
          .addComponent(adminPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel2)
          .addComponent(webPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel3)
          .addComponent(rdpPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addComponent(jLabel5)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addComponent(viewOnly)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(status)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
              .addComponent(save)
              .addComponent(Cancel)
              .addComponent(generateSSL)
              .addComponent(donate)))
          .addGroup(layout.createSequentialGroup()
            .addComponent(maxUsers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(0, 0, Short.MAX_VALUE)))
        .addContainerGap())
    );

    pack();
  }// </editor-fold>//GEN-END:initComponents

  private void saveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveActionPerformed
    writeConfig();
    restart();
    setVisible(false);
  }//GEN-LAST:event_saveActionPerformed

  private void CancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CancelActionPerformed
    setVisible(false);
  }//GEN-LAST:event_CancelActionPerformed

  private void generateSSLActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_generateSSLActionPerformed
    generateSSL();
  }//GEN-LAST:event_generateSSLActionPerformed

  private void donateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_donateActionPerformed
    JFAWT.donate();
  }//GEN-LAST:event_donateActionPerformed

  /**
   * @param args the command line arguments
   */
  public static void main(String args[]) {
    initSSL();
    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
        new Server();  //NOTE:Do NOT make it visible (it places icon in tray)
      }
    });
  }

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton Cancel;
  private javax.swing.JTextField adminPassword;
  private javax.swing.JButton donate;
  private javax.swing.JButton generateSSL;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JLabel jLabel3;
  private javax.swing.JLabel jLabel4;
  private javax.swing.JLabel jLabel5;
  private javax.swing.JSpinner maxUsers;
  private javax.swing.JTextField rdpPassword;
  private javax.swing.JButton save;
  private javax.swing.JLabel status;
  private javax.swing.JCheckBox viewOnly;
  private javax.swing.JTextField webPassword;
  // End of variables declaration//GEN-END:variables

  public SystemTray tray;
  public TrayIcon icon;
  public MenuItem exit, show;
  public Service service;

  private static String getSSLfile() {
    return JF.getUserPath() + "/.jfrdp.key";
  }

  private void generateSSL() {
    KeyMgmt.keytool(new String[] {
      "-genkey", "-keystore", getSSLfile(), "-keyalg", "RSA"
      , "-storepass", "changeme", "-keypass", "changeme"
      , "-dname", "CN=jfrdp.sourceforge.net,OU=jfRDP,O=jfRDP,C=CA"
      , "-validity", "3600"
    });
    sslValid();
  }

  private static void initSSL() {
    System.setProperty("javax.net.ssl.keyStore", getSSLfile());
    System.setProperty("javax.net.ssl.keyStorePassword", "changeme");
  }

  private boolean sslValid() {
    if (new File(getSSLfile()).exists()) {
      status.setText("Status : Ready");
      return true;
    }
    status.setText("Status : SSL Certficate not found");
    return false;
  }

  /** Encodes a password with some simple steps. */
  public static String encodePassword(String password) {
    char ca[] = password.toCharArray();
    int sl = ca.length;
    if (sl == 0) return "";
    char tmp;
    for(int p=0;p<sl/2;p++) {
      tmp = ca[p];
      ca[p] = ca[sl-p-1];
      ca[sl-p-1] = tmp;
    }
    StringBuffer out = new StringBuffer();
    for(int p=0;p<sl;p++) {
      ca[p] ^= 0xaa;
      if (ca[p] < 0x10) out.append("0");
      out.append(Integer.toString(ca[p], 16));
    }
//System.out.println("e1=" + out.toString());
    Random r = new Random();
    char key = (char)(r.nextInt(0xef) + 0x10);
    char outkey = key;
    ca = out.toString().toCharArray();
    sl = ca.length;
    for(int p=0;p<sl;p++) {
      ca[p] ^= key;
      key ^= ca[p];
    }
    out = new StringBuffer();
    for(int a=0;a<4;a++) {
      out.append(Integer.toString(r.nextInt(0xef) + 0x10, 16));
    }
    out.append(Integer.toString(outkey, 16));
    for(int p=0;p<sl;p++) {
      if (ca[p] < 0x10) out.append("0");
      out.append(Integer.toString(ca[p], 16));
    }
    for(int a=0;a<4;a++) {
      out.append(Integer.toString(r.nextInt(0xef) + 0x10, 16));
    }
    return out.toString();
  }
  /** Decodes a password. */
  public static String decodePassword(String crypto) {
    int sl = crypto.length();
    if (sl < 10) return null;
    char key = (char)(int)Integer.valueOf(crypto.substring(8,10), 16);
    char newkey;
    crypto = crypto.substring(10, sl - 8);
    int cl = (sl - 18) / 2;
    char ca[] = new char[cl];
    for(int p=0;p<cl;p++) {
      ca[p] = (char)(int)Integer.valueOf(crypto.substring(p*2, p*2+2), 16);
      newkey = (char)(key ^ ca[p]);
      ca[p] ^= key;
      key = newkey;
    }
    crypto = new String(ca);
//System.out.println("d1=" + crypto);
    cl = crypto.length() / 2;
    ca = new char[cl];
    for(int p=0;p<cl;p++) {
      ca[p] = (char)(int)Integer.valueOf(crypto.substring(p*2, p*2+2), 16);
    }
    for(int p=0;p<cl;p++) {
      ca[p] ^= 0xaa;
    }
    char tmp;
    for(int p=0;p<cl/2;p++) {
      tmp = ca[p];
      ca[p] = ca[cl-p-1];
      ca[cl-p-1] = tmp;
    }
    return new String(ca);
  }
}
